package com.tian.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.tian.client.SendCodeClient;
import com.tian.common.CommonResult;
import com.tian.dto.*;
import com.tian.entity.ChargeUser;
import com.tian.entity.GlobalProperty;
import com.tian.entity.PointsChangeRecord;
import com.tian.entity.SystemBusinessSwitch;
import com.tian.enums.*;
import com.tian.mapper.ChargeUserMapper;
import com.tian.mapper.GlobalPropertyMapper;
import com.tian.mapper.PointsChangeRecordMapper;
import com.tian.mapper.SystemBusinessSwitchMapper;
import com.tian.message.InvitedRegistryMessage;
import com.tian.message.MessageReqIdPrefixConstant;
import com.tian.producer.InvitedRegistryProducer;
import com.tian.service.ChargeUserService;
import com.tian.util.*;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RScoredSortedSet;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;


/**
 * {@code @description:} 用户相关操作服务实现类
 *
 * @author tianwc 公众号：Java后端技术全栈
 * 在线刷题 1200+java面试题和1000+篇技术文章：<a href="https://woaijava.cc/">博客地址</a>
 * {@code @date:} 2024/1/18 15:03
 * {@code @version:} 1.0
 */
@Slf4j
@Service
public class ChargeUserServiceImpl implements ChargeUserService {

    private static final String DEFAULT_PASSWORD = "123456";

    @Resource
    private ChargeUserMapper chargeUserMapper;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private InvitedRegistryProducer invitedRegistryProducer;
    @Resource
    private PointsChangeRecordMapper pointsChangeRecordMapper;
    @Resource
    private SystemBusinessSwitchMapper systemBusinessSwitchMapper;

    @Resource
    private GlobalPropertyMapper globalPropertyMapper;

    @Resource
    private SendCodeClient sendCodeClient;

    @Override
    public CommonResult<ChargeUserDto> queryById() {
        ChargeUserLoginResDto userInfoCache = UserCacheUtil.getUser();
        Long id = userInfoCache.getId();
        RBucket<String> bucket = redissonClient.getBucket(RedisConstantPre.USER_INFO_ID_PRE + id);
        if (bucket.get() == null) {
            ChargeUser chargeUser = chargeUserMapper.selectByPrimaryKey(id);
            ChargeUserDto chargeUserDto = ChargeUserConvert.convert(chargeUser);
            bucket.set(JSON.toJSONString(chargeUser));
            return CommonResult.success(chargeUserDto);
        }
        ChargeUser chargeUser = JSON.parseObject(bucket.get(), ChargeUser.class);
        ChargeUserDto chargeUserDto = ChargeUserConvert.convert(chargeUser);
        return CommonResult.success(chargeUserDto);
    }

    @Override
    public CommonResult<Boolean> register(ChargeUserRegistryReqDto chargeUserRegistryReqDto) {
        String phone = chargeUserRegistryReqDto.getPhone();
        String invitedCode = chargeUserRegistryReqDto.getInvitedCode();

        ValidCodeReqDto validCodeReqDto = new ValidCodeReqDto();
        validCodeReqDto.setPhone(phone);
        validCodeReqDto.setCode(chargeUserRegistryReqDto.getCode());
        CommonResult<Boolean> validResult = sendCodeClient.codeValid(validCodeReqDto);

        if (validResult.getCode() != ResultCode.SUCCESS.getCode()) {
            return validResult;
        }

        Date current = new Date();
        ChargeUser dbUser = chargeUserMapper.selectByPhone(phone);
        if (dbUser != null) {
            return CommonResult.failed(ResultCode.USER_EXISTED);
        }

        int point = 0;

        GlobalProperty globalPropertyRegistry = globalPropertyMapper.selectByBusiType(GlobalPropertyTypeEnums.REGISTRY.getType());
        if (globalPropertyRegistry != null) {
            point = globalPropertyRegistry.getBusiDefault();
        }

        //用户信息初始化
        ChargeUser user = new ChargeUser();
        user.setPhone(phone);
        user.setStatus(UserStatusEnum.INIT.getStatus());
        user.setAuthentication(UserAuthenticationEnum.INIT.getStatus());
        user.setVip(UserVipEnum.VIP.getStatus());
        user.setBalance(BigDecimal.ZERO);
        user.setPoint(point);
        user.setCreateTime(current);
        user.setNickName(ConstantUtil.DEFAULT_NICK + RandomUtil.randomNumbers(4));
        user.setInviteCode(ShareCodeUtils.idToCode(Long.getLong(phone)));
        user.setPassword(DigestUtils.md5DigestAsHex(DEFAULT_PASSWORD.getBytes()));
        chargeUserMapper.insert(user);

        ChargeUser chargeUser;
        if (StringUtil.isBlank(invitedCode)) {
            log.info("没有注册邀请码");
            return CommonResult.success(Boolean.TRUE);
        }
        chargeUser = chargeUserMapper.queryByInvitedCode(invitedCode);
        if (chargeUser == null) {
            log.error("邀请码不存在 code = {} ", invitedCode);
            return CommonResult.failed(ResultCode.INVITED_CODE_NO_EXIST);
        }
        SystemBusinessSwitch systemBusinessSwitch = systemBusinessSwitchMapper.selectByType(SystemBusinessSwitchTypeEnum.INVITED_REGISTRY_INCOME.getType());
        if (systemBusinessSwitch.getOpen() == SystemBusinessSwitchOpenEnum.CLOSE.getType()) {
            log.info("业务type={} 名称={} 已开关已关闭", systemBusinessSwitch.getType(), systemBusinessSwitch.getDesc());
            return CommonResult.success(Boolean.TRUE);
        }

        BigDecimal income = BigDecimal.ZERO;

        GlobalProperty globalPropertyInvited = globalPropertyMapper.selectByBusiType(GlobalPropertyTypeEnums.INVITED_REGISTRY_INCOME.getType());
        if (globalPropertyInvited != null) {
            income = new BigDecimal(globalPropertyInvited.getBusiDefault());
        }

        InvitedRegistryMessage invitedRegistryMessage = new InvitedRegistryMessage();
        invitedRegistryMessage.setAmount(income);
        invitedRegistryMessage.setUserId(chargeUser.getId());
        invitedRegistryMessage.setNewUserId(user.getId());
        invitedRegistryMessage.setCreateTime(current);
        invitedRegistryMessage.setReqId(MessageReqIdPrefixConstant.USER_INVITED_REGISTRY_REQ_ID_PREFIX + UUID.randomUUID() + DateUtils.formatDefaultDateMs());
        invitedRegistryProducer.send(invitedRegistryMessage);
        return CommonResult.success(Boolean.TRUE);
    }

    @Override
    public CommonResult<ChargeUserLoginResDto> login(String phone) {

        return null;
    }

    @Override
    public CommonResult<Boolean> updateById(ChargeUserDto chargeUserDto) {
        ChargeUser chargeUser = new ChargeUser();
        BeanUtil.copyProperties(chargeUserDto, chargeUser);
        chargeUserMapper.updateById(chargeUser);
        RBucket<String> bucket = redissonClient.getBucket(RedisConstantPre.USER_INFO_ID_PRE + chargeUserDto.getId());

        ChargeUserLoginResDto chargeUserLoginResDto = new ChargeUserLoginResDto();
        BeanUtils.copyProperties(chargeUserMapper.selectByPrimaryKey(chargeUserDto.getId()), chargeUserLoginResDto);
        bucket.set(JSON.toJSONString(chargeUserLoginResDto));
        return CommonResult.success(Boolean.TRUE);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public CommonResult<Boolean> updatePoint(UpdatePointReqDto updatePointReqDto) {

        ChargeUser chargeUser = chargeUserMapper.selectByPrimaryKey(updatePointReqDto.getUserId());
        if (chargeUser == null) {
            return CommonResult.failed(ResultCode.USER_NO_EXIST);
        }
        if (chargeUser.getStatus() == UserStatusEnum.DELETE.getStatus()) {
            return CommonResult.failed(ResultCode.USER_DELETE);
        }

        if (updatePointReqDto.getType() == UserUpdatePointEnum.DEDUCT.getType()) {
            if (chargeUser.getPoint() < updatePointReqDto.getPoint()) {
                return CommonResult.failed(ResultCode.ACTIVITY_NO_ENOUGH);
            }
            chargeUser.setPoint(chargeUser.getPoint() - updatePointReqDto.getPoint());
        } else {
            chargeUser.setPoint(chargeUser.getPoint() + updatePointReqDto.getPoint());
        }
        chargeUserMapper.updateByPrimaryKey(chargeUser);

        PointsChangeRecord pointsChangeRecord = new PointsChangeRecord();
        pointsChangeRecord.setUserId(chargeUser.getId());
        pointsChangeRecord.setPoint(updatePointReqDto.getPoint());
        pointsChangeRecord.setType(updatePointReqDto.getType());
        pointsChangeRecordMapper.insert(pointsChangeRecord);

        return CommonResult.success(Boolean.TRUE);
    }

    @Override
    public CommonResult<ChargeUserPointsRankListRespDto> userPointRank() {
        ChargeUserPointsRankListRespDto chargeUserPointsRankListRespDto = new ChargeUserPointsRankListRespDto();
        RScoredSortedSet<Long> leaderboard = redissonClient.getScoredSortedSet("user.point.rank");
        List<ChargeUserPointsRankRespDto> chargeUserPointsRankRespDtoList = new ArrayList<>();

        AtomicLong rank = new AtomicLong(leaderboard.stream().count());
        leaderboard.entryRange(0, -1).forEach(entry -> {
            ChargeUserPointsRankRespDto chargeUserPointsRankRespDto = new ChargeUserPointsRankRespDto();
            chargeUserPointsRankRespDto.setUserId(entry.getValue());
            chargeUserPointsRankRespDto.setRank(rank.intValue());
            chargeUserPointsRankRespDtoList.add(chargeUserPointsRankRespDto);
            rank.getAndDecrement();
        });
        List<Long> userIdList = chargeUserPointsRankRespDtoList.stream()
                .map(ChargeUserPointsRankRespDto::getUserId)
                .collect(Collectors.toList());
        List<ChargeUser> chargeUserList = chargeUserMapper.selectByIdList(userIdList);

        Map<Long, ChargeUser> pointsChangeRecordMap = chargeUserList.stream()
                .collect(Collectors.toMap(ChargeUser::getId, record -> record));

        for (ChargeUserPointsRankRespDto chargeUserPointsRankRespDto : chargeUserPointsRankRespDtoList) {
            ChargeUser chargeUser = pointsChangeRecordMap.get(chargeUserPointsRankRespDto.getUserId());
            chargeUserPointsRankRespDto.setPhone(chargeUser.getPhone());
            chargeUserPointsRankRespDto.setNickName(chargeUser.getNickName());
            chargeUserPointsRankRespDto.setAvatarImg(chargeUser.getAvatarImg());
            chargeUserPointsRankRespDto.setPoints(chargeUser.getPoint());
        }
        chargeUserPointsRankListRespDto.setChargeUserPointsRankRespDtoList(chargeUserPointsRankRespDtoList);
        return CommonResult.success(chargeUserPointsRankListRespDto);
    }

    @Override
    public CommonResult<ChargeUsersRespDto> findUserByUserIdList(ChargeUsersReqDto chargeUsersReqDto) {
        List<ChargeUser> chargeUserList = chargeUserMapper.selectByIdList(chargeUsersReqDto.getUserIdList());
        ChargeUsersRespDto chargeUsersRespDto=new ChargeUsersRespDto();
        if(!CollectionUtils.isEmpty(chargeUserList)){
            List<ChargeUserInfoDto> chargeUserInfoDtoList=new ArrayList<>();
            for(ChargeUser chargeUser:chargeUserList){
                ChargeUserInfoDto chargeUserInfoDto=new ChargeUserInfoDto();
                BeanUtils.copyProperties(chargeUser,chargeUserInfoDto);
                chargeUserInfoDtoList.add(chargeUserInfoDto);
            }
            chargeUsersRespDto.setChargeUserInfoDtoList(chargeUserInfoDtoList);
        }
        return CommonResult.success(chargeUsersRespDto);
    }


}
